home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Technical Documentation / Sample Code / DTS_SCSI Code (In Development) / DTS_SCSI_IO.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-15  |  15.6 KB  |  396 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        DTS_SCSI_IO.c
  3.     
  4.     
  5.     
  6.     
  7.     
  8.     Unfortunately, no matter how long awaited, it's still not done.  In fact, this
  9. isn't even a release- this is just an image of the code taken in the middle of
  10. development.
  11.  
  12. THIS CODE DOES NOT WORK AS A WHOLE.  MUCH OF IT IS BUGGY AND / OR INCOMPLETE.
  13. YOU WOULD HAVE TO BE ABSOLUTELY INSANE TO USE ANY OF THIS CODE IN YOUR
  14. PROJECT WITHOUT EXTENSIVE THOUGHT, DEBUGGING AND TESTING.
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.     Contains:    Low-level SCSI input/output code to implement a SCSI Manager
  22.                 client, for the DTS SCSI Driver and its Formatting Application.
  23.                 All access to the SCSI Manager should go through these routines.
  24.                 
  25.                 This file contains these public routines:
  26.                         
  27.                     StuffSCSICommandBlock:
  28.                         Handy for initializing SCSI command descriptor blocks.
  29.                         The corresponding StuffTransferInstructionBlock is used
  30.                         within SCSIOp.
  31.                         
  32.                     SCSIOp: does the complete low-level SCSI sequence, with
  33.                         external control over timeout.
  34.                     
  35.                     SCSIPrime: does block I/O to/from the drive, using SCSIOp.
  36.                 
  37.                 This code is compiled in two flavors. If "-d INCLUDEVMCALLS" is
  38.                 defined on the compiler command line, SCSIOp includes calls
  39.                 to "hold" itself and its data structures during SCSI Manager calls.
  40.                 Normally, drivers don't need to worry about this because VM's
  41.                 Device Manager patches take care of holding the parameter block,
  42.                 data buffers, and the driver itself, but if we're including this
  43.                 code in an application, the calls are important.
  44.                 
  45.                 Note that because this code can be compiled to be part of the
  46.                 driver or of the application, no access to global storage is
  47.                 allowed; all calls here depend only on their arguments and do
  48.                 no allocation (other than on the stack for local variables).
  49.  
  50.     Written by:    Mike Bell, Neil Day, Colleen Delgadillo, Tim Dierks, Dennis 
  51.                 Hescox, Craig Prouse, Kent Sandvik, Bryan Stearns
  52.  
  53.     Copyright:    © 1992 by Apple Computer, Inc., all rights reserved.
  54.  
  55.     Change History (most recent first):
  56.  
  57.                  3/14/92    BJS        Combined code by Messrs Prouse & Sandvik
  58.  
  59.     To Do:
  60. */
  61.  
  62. #include <Memory.h>
  63. #include <GestaltEqu.h>
  64. #include "DTS_SCSI_IO.h"
  65.  
  66. #ifdef INCLUDEVMCALLS
  67. //
  68. // Is VM running?
  69. // If we get an error from Gestalt, VM can't possibly be around.
  70. //
  71. Boolean IsVirtualMemoryRunning() {
  72.     long vmState;
  73.     short error = Gestalt(gestaltVMAttr, &vmState); // test for VM
  74.     return (error == noErr) ? vmState : false; // return false if Gestalt couldn't tell us.
  75. }
  76. #endif
  77.  
  78. //
  79. // Fill in a SCSI Command block
  80. //
  81. void StuffSCSICommandBlock(SCSICommandBlock cmd, unsigned char a, unsigned char b,
  82.                             unsigned char c, unsigned char d, unsigned char e, unsigned char f) {
  83.     // Just stuff the six bytes in order.
  84.     cmd[0] = a; cmd[1] = b; cmd[2] = c; cmd[3] = d; cmd[4] = e; cmd[5] = f;
  85. }
  86.  
  87. //
  88. // Fill in a Transfer Instruction Block. if the loopCount is nonzero, the TIB is
  89. // expected to be three instructions:
  90. //
  91. //        @0: scInc    buffer, blockSize
  92. //        @1:    scLoop    @0, loopCount
  93. //        @2: scStop    0, 0
  94. // 
  95. // If loopCount is zero, the middle instruction is omitted (it's safe to pass in
  96. // a two-instruction TIB array in this case).
  97. //
  98. void StuffTransferInstructionBlock(TransferInstructionBlock tib, void *buffer, unsigned
  99.                                     long blockSize, unsigned long loopCount) {
  100.     register SCSIInstr *instr = tib;
  101.     
  102.     instr->scOpcode = scInc;
  103.     instr->scParam1 = (unsigned long) buffer;
  104.     (instr++)->scParam2 = blockSize;
  105.     if (loopCount != 0) {
  106.         instr->scOpcode = scLoop;
  107.         instr->scParam1 = - sizeof(SCSIInstr);
  108.         (instr++)->scParam2 = loopCount;
  109.     }
  110.     instr->scOpcode = scStop;
  111.     instr->scParam1 = instr->scParam2 = 0;
  112. }
  113.  
  114.  
  115. //
  116. //    This is an example of a subroutine to perform SCSI transactions.  It works for
  117. //    well-behaved targets which follow a predictable phase sequence, including most
  118. //    Apple hard disks.  It may require modifications for other device types such as
  119. //    SCSI-2 devices or CD-ROM drives.
  120. //
  121. //    Use of this subroutine requires some knowledge of the SCSI specification and the
  122. //    SCSI Manager because the structure of a Command Descriptor Block and a Transfer
  123. //    Instruction Block are not documented here.
  124. //
  125. //    If virtual memory is running, everything needed to complete the SCSI transaction
  126. //    must be held in physical memory, to prevent a page fault from occurring while the
  127. //    SCSI bus is in use:
  128. //        * For drivers, VM's patches to the Device Manager will take care of holding 
  129. //          the parameter block, the data buffer (ioBuffer[ioReqCount]), and 512 bytes 
  130. //          of stack space; the device control entry, DCtlStorage, and the driver code 
  131. //          itself are in the system heap, which is always held.
  132. //
  133. //        * An application that makes SCSI Manager calls is responsible for holding all 
  134. //          of these structures itself. The code to do this is conditionally compiled
  135. //          into this procedure: when this file is compiled for the application, the
  136. //          compiler command line includes "-d INCLUDEVMCALLS" to trigger the
  137. //          inclusion of this code, which holds the following structures:
  138. //            * the SCSIOp code itself
  139. //            * the SCSI Command Descriptor Block (assumed to be 16 bytes or less)
  140. //            * the data buffer, if any
  141. //            * the local SCSIOp stack area
  142. //    
  143. //    The strangest part of this holding operation is guessing the size of our stack
  144. //    requirements: we can't know the amount of stack that the SCSI Manager will need
  145. //    when we call it, nor can we find out from the compiler exactly how big our
  146. //    stack frame is. So, we estimate both:
  147. #define kSCSIManagerStackEstimate 512
  148. #define kSCSIOpStackFrameSize 64
  149. //
  150. //    Parameters (all input):
  151. //
  152. //        cmd: Points at a SCSI Command Descriptor Block, filled in for this operation.
  153. //        cmdSize: Size of the Command Descriptor Block (usually 6, 10, or 12 bytes).
  154. //      targetSCSIID: SCSI bus ID of the requested target (0 - 6).
  155. //      completionTimeout: timeout to pass in our call to SCSIComplete.
  156. //        callback: idle procedure, used by Format to spin the cursor.
  157. //        buffer: Points at the data buffer to be used, if any (NULL if none).
  158. //      If buffer is NULL, the rest of these fields are ignored:
  159. //        blockSize: The block size of the data to be transferred
  160. //        loopCount: The number of blocks to be transferred, or 0 if the data is one block or less.
  161. //      writing: true if we're writing, false if we're reading.
  162. //      blind: true if we're doing a blind transfer, false if we're polling.
  163. //
  164. //    Result:
  165. //        0:        No error (GOOD) 
  166. //        < 0:    Negated versions of SCSI Manager error result codes
  167. //        > 0:    SCSI status byte (e.g. CHECK CONDITION, BUSY, etc.)
  168.         
  169. OSErr SCSIOp (SCSICommandBlock cmd, short cmdSize, SCSIAddress targetSCSIID, unsigned long 
  170.               completionTimeout, void *buffer, unsigned long blockSize, 
  171.               unsigned long loopCount, Boolean writing, Boolean blind) {
  172.     SCSIInstr    tib[3];                // The Transfer Instruction Block we'll use
  173.     short status, message;            // Results from SCSIComplete
  174.     OSErr compErr, opErr;            // Error codes along the way
  175. #ifdef INCLUDEVMCALLS            //*** Document usage of INCLUDEVMCALLS
  176.     short holdErr = noErr;            // success in holding memory
  177.     short heldCount;                // number of accomplished HoldMemory calls
  178.     unsigned long bufferHeld;        // length of buffer to hold
  179.     extern void NextFunction();     // a dummy function -- tells us where this one ends.
  180.     short protectedStackSize;        // How much we held for our stack
  181.     char *protectedStackBase;        //    and where it starts
  182.     Boolean vmOn = IsVirtualMemoryRunning();    // Is VM on?
  183.  
  184.     // *** for debugging safety: we don't allow any commands other than Test and
  185.     // Inquiry for addresses other than 6
  186.     if ((targetSCSIID != 6) && (cmd[0] != SCSICmd_TestUnitReady) && (cmd[0] != SCSICmd_Inquiry))
  187.         debugstr("DANGER, WILL ROBINSON! Doing unsafe command to unsafe unit!"); //*** should be an assert.
  188.                 
  189.     if (vmOn) {    // Hold everything!
  190.         // We need to be able to back out if we get an error during these "holds"
  191.         heldCount = 0; // we'll keep track of 
  192.         
  193.         // First, our code. It starts at SCSIOp and continues through the beginning 
  194.         // of the next function, which is a dummy function we've included just for 
  195.         // this purpose.
  196.         holdErr = HoldMemory(SCSIOp, (unsigned long) NextFunction - (unsigned long) SCSIOp);
  197.         if (holdErr == noErr) {
  198.             heldCount = 1;
  199.         
  200.             // The SCSI command block is next. We assume that it's less than 16 bytes.
  201.             holdErr = HoldMemory(cmd, 16);
  202.             if (holdErr == noErr) {
  203.                 heldCount = 2;
  204.                 
  205.                 // The data buffer starts at "buffer" for at least "blockSize" bytes.
  206.                 // If "loopCount" is non-zero, we multiply the blocksize by it, too.
  207.                 
  208.                 bufferHeld = loopCount ? blockSize * loopCount : blockSize;
  209.                 
  210.                 holdErr = HoldMemory(buffer, bufferHeld);
  211.                 if (holdErr == noErr) {
  212.                     heldCount = 3;
  213.                     
  214.                     // Finally, the stack. We know we'll need some stack space to call
  215.                     // the SCSI Manager, and we'll need to protect our local variables.
  216.                     // So, we get the address of one of our local variables (they're on
  217.                     // the stack), subtract the amount of space we think the SCSI Manager
  218.                     // will need, then hold from there to above our locals by a fair margin.
  219.                     protectedStackBase = ((char *) &vmOn) - kSCSIManagerStackEstimate;
  220.                     protectedStackSize = kSCSIManagerStackEstimate + kSCSIOpStackFrameSize;
  221.                     holdErr = HoldMemory(protectedStackBase, protectedStackSize);
  222.                     if (holdErr == noErr) {
  223.                         heldCount = 4;
  224.                     }
  225.                 }
  226.             }
  227.         }
  228.     }
  229.  
  230.     // Did all the holds succeed?
  231.     if (holdErr == noErr)
  232. #endif
  233.     {
  234.         // If we're transferring data, make a Transfer Instruction Block, so 
  235.         // that the SCSI Manager will know where to get (or put) the data.
  236.         // (Yes, StuffTransferInformationBlock is outside of the protected RAM.
  237.         // This doesn't matter, because we haven't tied up the bus yet.)
  238.         if (buffer != NULL)
  239.             StuffTransferInstructionBlock(tib, buffer, blockSize, loopCount);
  240.  
  241.         // Now we'll do the SCSI Operation:
  242.         // Arbitrate for control of the SCSI bus
  243.         opErr = SCSIGet();
  244.         if (opErr != noErr) {
  245.             // We couldn't get the bus; skip out.
  246.             return -opErr;
  247.         }
  248.         
  249.         // We got the bus, so we'll have to call SCSIComplete at some point to release it.
  250.         // Our next step is to select the specified target.
  251.         opErr = SCSISelect(targetSCSIID);
  252.         if (opErr == noErr) {
  253.             // We've succeeded in finding our target -- send the command to it.
  254.             opErr = SCSICmd(cmd, cmdSize);
  255.             
  256.             // If the caller expected data, we'll have gotten a pointer to a Transfer
  257.             // Instruction Block; transfer the data, if any.
  258.             if ((opErr == noErr) && (buffer != NULL)) {                
  259.                 // This is a four-way selection, selecting between two pairs of
  260.                 // two operations, based on whether we're reading or writing and
  261.                 // whether we're doing a blind or polled transfer.
  262.                 opErr = writing ?
  263.                             (blind ? SCSIWBlind((Ptr)tib) : SCSIWrite((Ptr)tib)) :
  264.                             (blind ? SCSIRBlind((Ptr)tib) : SCSIRead((Ptr)tib));
  265.             }
  266.             
  267.             
  268.             // Complete the transaction and release the bus
  269.             compErr = SCSIComplete(&status, &message, completionTimeout);
  270.                 
  271.             // Must call SCSIComplete once SCSISelect succeeds, but
  272.             // its result code should not mask any previous errors.
  273.             if (opErr == noErr)
  274.                 opErr = compErr;
  275.         }
  276.     }
  277.  
  278. #ifdef INCLUDEVMCALLS
  279.     // We're done. Unhold the memory we held, in the reverse order that we
  280.     // held it. Ignore any errors along the way.  Note that we fall through
  281.     // cases to unhold all the already held sections.
  282.     switch (heldCount) {
  283.         case 4:
  284.             UnholdMemory(protectedStackBase, protectedStackSize);
  285.         case 3:
  286.             UnholdMemory(buffer, bufferHeld);
  287.         case 2:
  288.             UnholdMemory(cmd, 16);
  289.         case 1:
  290.             UnholdMemory(SCSIOp, (unsigned long) NextFunction - (unsigned long) SCSIOp);
  291.     }
  292.     
  293.     // If we got an error while holding the memory, return it now.
  294.     if (holdErr != noErr) 
  295.         return holdErr;
  296. #endif
  297.  
  298.     //    Attempt to return the most valuable result possible.  A SCSI Manager error is
  299.     //    negated to distinguish it from a positive SCSI status byte code, which may be
  300.     //    returned for a failed transaction even if the SCSI Manager is "successful."
  301.     //
  302.     //    If there were no errors from the SCSI Manager, the result is the SCSI status
  303.     //    byte, otherwise it is the negation of the SCSI Manager error.
  304.     //
  305.     return opErr == noErr ? status : -opErr;
  306. }
  307. // A dummy function; we use it to find the end of the previous function, SCSIOp, when
  308. // holding its memory. 
  309. void NextFunction() { }
  310.  
  311. //
  312. //    Transfer data over the SCSI bus. This is a wrapper for SCSIOp, above, that
  313. //    knows that we're doing a read or write of data to the device. The data
  314. //    buffer must be held in physical memory (if this call is being made by a disk
  315. //    driver as a result of a _Read or _Write operation, VM will take care of this
  316. //    for us).
  317. //    
  318. //    Parameters:
  319. //        dataStart: Pointer to the start of the data to be transferred.
  320. //        targetSCSIID: SCSI bus ID of the requested target.
  321. //        targetUnit:    Unit number of the requested target (usually 0).
  322. //        
  323. //        cmd: Pointer to a SCSICommandBlock, held in memory.
  324. //        cmdSize: Size of the SCSICommandBlock (usually 6 bytes).
  325. //        tib: Pointer to a TransferInstructionBlock, or NIL if none. Held in memory.
  326. //      targetSCSIID: SCSI bus ID of the requested target (0 - 6).
  327. //        startBlock: source or destination starting block number.
  328. //      blockCountPtr: Pointer to the number of blocks to be transferred (updated on exit).
  329. //        blockSize: Size of each block on this device.
  330. //        writing: TRUE if we're writing, FALSE if we're reading, ignored if transferInstructionBlock 
  331. //            is NIL.
  332. //      blind: TRUE if we're doing a blind transfer, false if we're polling. Ignored if 
  333. //            transferInstructionBlock = NIL.
  334. //      completionTimeout: timeout to pass in our call to SCSIComplete.
  335. //
  336. OSErr SCSIPrime(void *dataStart, SCSIAddress targetSCSIID, short targetUnit, unsigned long startBlock, 
  337.                 unsigned long *blockCountPtr, unsigned long blockSize, Boolean writing, Boolean blind, 
  338.                 unsigned long completionTimeout) {
  339.     SCSICommandBlock cmd;
  340.     unsigned long reqBlocks, chunkBlocks, nextBlock;
  341.     OSErr opErr = noErr;
  342.     unsigned char *buffer = dataStart;
  343.     
  344.     // SCSI's low-level transfer operations are only capable of transferring a maximum of
  345.     // 256 blocks, so we loop over each set of 256 blocks. (Note that in the actual SCSI
  346.     // command descriptor block, a block count of zero means "256").
  347.     reqBlocks = *blockCountPtr;
  348.     nextBlock = startBlock;
  349.     while (opErr == noErr && reqBlocks > 0) {    // While we have blocks left...
  350.         // Figure out how many blocks we'll be transferring this iteration.
  351.         chunkBlocks = (reqBlocks <= 256) ? reqBlocks : 256;
  352.         
  353.         //
  354.         // Set up the SCSI command block
  355.         //
  356.         // Byte  Bits:  7   6   5   4   3   2   1   0
  357.         //            +-------------------------------+
  358.         //  0         | Oper. code: Read=08, Write=0A |
  359.         //  1         | Log. Unit | Block Addr (high) |
  360.         //  2         | Logical Block Address (mid)   |
  361.         //  3         | Logical Block Address (low)   |
  362.         //  4         |         Block count           |
  363.         //  5         |    Control byte (always 0)    |
  364.         //            +-------------------------------+
  365.         //
  366.         // (Thanks to Zaydoon)
  367.         
  368.         cmd[0] = writing ? SCSICmd_Write : SCSICmd_Read; // Command code
  369.         cmd[1] = (targetUnit << 5) | (nextBlock >> 16);     // Logical unit & bits 20-16 of address
  370.         cmd[2] = nextBlock >> 8;                         // Bits 15-8 of address
  371.         cmd[3] = nextBlock;                                 // Bits 7-0 of address
  372.         cmd[4] = chunkBlocks;                             // Block count (256 will be truncated to 0)
  373.         cmd[5] = 0;                                         // Not a linked command
  374.         
  375.         //
  376.         // Finally, we do the transfer
  377.         //
  378.         opErr = SCSIOp(&cmd, 6 /* commandSize */, targetSCSIID, completionTimeout, 
  379.                          buffer, blockSize, 
  380.                         chunkBlocks, writing, blind);
  381.         
  382.         // If we were successful, update our running counters and buffer pointer.
  383.         if (opErr == noErr) {
  384.             nextBlock += chunkBlocks;
  385.             buffer += (chunkBlocks * blockSize);
  386.             reqBlocks -= chunkBlocks;
  387.         }
  388.     }
  389.     
  390.     *blockCountPtr -= reqBlocks;        // indicate the actual number of blocks transferred
  391.     return opErr;
  392. }
  393.  
  394. #ifdef INCLUDEVMCALLS
  395. void LowLevelHoldEnd() { /* dummy routine - see HoldLowLevelCode(). */ }
  396. #endif